package com.campuscrowdsourcing.server.service.impl;

import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.RandomUtil;
import com.campuscrowdsourcing.server.common.constant.CacheConst;
import com.campuscrowdsourcing.server.common.constant.CommonConst;
import com.campuscrowdsourcing.server.common.constant.HttpStatus;
import com.campuscrowdsourcing.server.common.constant.RoleConst;
import com.campuscrowdsourcing.server.common.enums.Gender;
import com.campuscrowdsourcing.server.common.expection.ServiceException;
import com.campuscrowdsourcing.server.common.mail.MailTemplate;
import com.campuscrowdsourcing.server.domain.dto.user.LoginDTO;
import com.campuscrowdsourcing.server.domain.entity.User;
import com.campuscrowdsourcing.server.domain.model.LoginUser;
import com.campuscrowdsourcing.server.domain.vo.FileVO;
import com.campuscrowdsourcing.server.domain.vo.TeamUserVO;
import com.campuscrowdsourcing.server.domain.vo.TokenVO;
import com.campuscrowdsourcing.server.domain.vo.UserVO;
import com.campuscrowdsourcing.server.mapper.TeamMapper;
import com.campuscrowdsourcing.server.mapper.TeamRoleMapper;
import com.campuscrowdsourcing.server.mapper.UserMapper;
import com.campuscrowdsourcing.server.service.SystemFileService;
import com.campuscrowdsourcing.server.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.campuscrowdsourcing.server.utils.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.TimeUnit;


/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Xinyuan Zhao
 * @since 2023-03-09
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private static  final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Resource
    private MailUtil mailUtil;
    @Resource
    private RedisUtil redisUtil;

    @Resource
    private SystemFileService systemFileService;


    /**
     * 用户名、邮箱、手机号/密码登录
     *
     * @param loginDto  登录参数
     * @return 用户信息
     */
    @Override
    public UserVO login(LoginDTO loginDto) {
        // 查找用户
        User one = lambdaQuery().and(wrapper -> wrapper
                        .eq(User::getUsername, loginDto.getAccount())
                        .or()
                        .eq(User::getEmail, loginDto.getAccount())
                        .or()
                        .eq(User::getPhoneNumber, loginDto.getAccount()))
                .one();
        if (one == null) {
            throw new ServiceException(HttpStatus.BAD_REQUEST, "账号未注册");
        }
        if (one.getPassword() == null){
            throw new ServiceException(HttpStatus.BAD_REQUEST, "请使用验证码登录后设置密码");
        }
        //验证密码
        if (!SecurityUtil.matchesPassword(loginDto.getPassword(),one.getPassword())){
            throw new ServiceException(HttpStatus.BAD_REQUEST, "密码错误");
        }

        TokenVO tokenVO = createToken(one);
        // 返回用户信息
        UserVO userVo = new UserVO();
        BeanUtils.copyProperties(one, userVo);
        userVo.setAccessToken(tokenVO.getAccessToken());
        userVo.setRefreshToken(tokenVO.getRefreshToken());
        // 信息脱敏
        userVo.setEmail(DesensitizedUtil.email(userVo.getEmail()));
        userVo.setPhoneNumber(DesensitizedUtil.mobilePhone(userVo.getPhoneNumber()));
        return userVo;
    }

    /**
     * 用户注册
     * @param loginDto 注册参数
     * @return 用户信息
     */
    @Override
    public UserVO register(LoginDTO loginDto){
        if(!StringUtils.hasText(loginDto.getAccount())){
            throw new ServiceException();
        }
        boolean isEmail = loginDto.getAccount().matches(CommonConst.REGEXP_EMAIL);
        boolean isPhone = loginDto.getAccount().matches(CommonConst.REGEXP_PHONE);
        if(!isEmail && !isPhone){
            throw new ServiceException("请使用邮箱/手机号");
        }
        // 查找缓存验证码
        Integer code = isEmail ?
                redisUtil.getCacheObject(CacheConst.EMAIL_CODE + loginDto.getAccount()) :
                redisUtil.getCacheObject(CacheConst.PHONE_CODE + loginDto.getAccount());
        if(code == null){
            throw new ServiceException("请获取验证码");
        }
        if(!code.equals(Integer.valueOf(loginDto.getCode()))){
            throw new ServiceException("验证码错误");
        }
        // 删除验证码
        redisUtil.deleteObject(isEmail ?
                CacheConst.EMAIL_CODE + loginDto.getAccount() :
                CacheConst.PHONE_CODE + loginDto.getAccount());

        // 登录/注册
        User one = lambdaQuery()
                .eq(User::getEmail,loginDto.getAccount())
                .or()
                .eq(User::getPhoneNumber,loginDto.getAccount())
                .one();
        // 没注册
        if(one == null){
            one = new User();
            String swayId = getAccount();
            one.setUsername(swayId);
            one.setUid(swayId);
            // 设置邮箱或手机号
            if(isEmail){
                one.setEmail(loginDto.getAccount());
            }else{
                one.setPhoneNumber(loginDto.getAccount());
            }
            one.setAvatar("http://localhost:9000/image/73e792958d3d48e487af55bb5ec654b7.webp");
            // 性别默认保密
            one.setGender(Gender.GENDER_OTHER.getGender());
            one.setCreateTime(LocalDateTime.now());
            one.setUserRole(RoleConst.ROLE_USER);
            one.setUserStatus(true);
            one.setDeleted(false);
            try{
                save(one);
            }catch (Exception e){
                throw new ServiceException();
            }
        }
        TokenVO tokenVO = createToken(one);
        // 返回用户信息
        UserVO userVo = new UserVO();
        BeanUtils.copyProperties(one, userVo);
        userVo.setAccessToken(tokenVO.getAccessToken());
        userVo.setRefreshToken(tokenVO.getRefreshToken());
        // 信息脱敏
        userVo.setEmail(DesensitizedUtil.email(userVo.getEmail()));
        userVo.setPhoneNumber(DesensitizedUtil.mobilePhone(userVo.getPhoneNumber()));
        return userVo;
    }


    /**
     * 更新用户密码
     * @param loginDTO 修改参数
     */
    @Override
    public void updatePassword(LoginDTO loginDTO){
        try{
            // 查找用户
            Integer userId = RequestUtil.getCurrentUserId();
            User one = lambdaQuery().eq(User::getId, userId).one();
            if(one == null){
                throw new ServiceException("用户不存在");
            }
            // 校验验证码
            Integer emailCode = redisUtil.getCacheObject(CacheConst.EMAIL_CODE + one.getEmail());
            Integer phoneCode = redisUtil.getCacheObject(CacheConst.PHONE_CODE + one.getPhoneNumber());
            if(emailCode == null && phoneCode == null){
                throw new ServiceException("请获取验证码");
            }
            if(!Integer.valueOf(loginDTO.getCode()).equals(emailCode) &&
                    !Integer.valueOf(loginDTO.getCode()).equals(phoneCode)){
                throw new ServiceException("验证码错误");
            }
            // 删除验证码
            redisUtil.deleteObject(phoneCode == null ?
                    CacheConst.EMAIL_CODE + one.getEmail() :
                    CacheConst.PHONE_CODE + one.getPhoneNumber());
            // 修改密码
            loginDTO.setPassword(SecurityUtil.encodePassword(loginDTO.getPassword()));
            lambdaUpdate().eq(User::getId,userId)
                    .set(User::getPassword,loginDTO.getPassword())
                    .update();
        }catch (Exception e){
            throw new ServiceException(e.getMessage());
        }
    }


    /**
     * 获取验证码
     * @param account 邮箱/手机
     */
    @Override
    public void getCode(String account){
        // 分析邮箱或手机号
        int code = new Random().nextInt(900000) + 100000;
        // 邮箱
        List<String> mail = new ArrayList<>();
        mail.add(account);
        redisUtil.setCacheObject(CacheConst.EMAIL_CODE + account,code, CommonConst.TOKEN_CODE, TimeUnit.MINUTES);
        mailUtil.sendMailMessage(mail,"校园众包", MailTemplate.emailHtml(String.valueOf(code)));
    }


    /**
     * 生成Token并存入Redis
     *
     * @param user 登录用户
     * @return token
     */
    private TokenVO createToken(User user){
        TokenVO tokenVO = new TokenVO();
        tokenVO.setAccessToken(TokenUtil.createToken(user.getId(), CommonConst.TOKEN_EXPIRE_ACCESS));
        tokenVO.setRefreshToken(TokenUtil.createToken(user.getId(), CommonConst.TOKEN_EXPIRE_REFRESH));
        redisUtil.setCacheObject(CacheConst.LOGIN_USER_KEY + user.getId(), user,CommonConst.TOKEN_EXPIRE_REFRESH, TimeUnit.DAYS);
        // 请求token有效期2小时
        redisUtil.setCacheObject(CacheConst.LOGIN_TOKEN_ACCESS_KEY + user.getId(), tokenVO.getAccessToken(), CommonConst.TOKEN_EXPIRE_ACCESS, TimeUnit.HOURS);
        redisUtil.setCacheObject(CacheConst.LOGIN_TOKEN_REFRESH_KEY + user.getId(), tokenVO.getRefreshToken(), CommonConst.TOKEN_EXPIRE_REFRESH, TimeUnit.DAYS);
        return tokenVO;
    }


    /**
     * 生成UUID
     * @return 用户账号
     */
    public String getAccount(){
        String account = RandomUtil.randomNumbers(8);
        User one = lambdaQuery().eq(User::getUid,account).one();
        return one == null ? account : getAccount();
    }


    /**
     * 更换头像
     * @param file 头像
     */
    @Override
    public  void updateAvatar(MultipartFile file){
        Integer userId = RequestUtil.getCurrentUserId();
        Integer count =  lambdaQuery().eq(User::getId, userId)
                .eq(User::getDeleted, false).count();
        if(count == 0){
            throw new ServiceException();
        }
        FileVO fileVo = systemFileService.uploadImageFile(file, userId);
        if(fileVo == null){
            throw new ServiceException();
        }
        try{
            lambdaUpdate().eq(User::getId, userId)
                    .eq(User::getDeleted,false)
                    .set(User::getAvatar, fileVo.getUrl())
                    .update();
        }
        catch (Exception e){
            throw new ServiceException();
        }
    }

    /**
     * 刷新token
     * @return 双token
     */
    @Override
    public TokenVO refreshToken(){
        Integer userId = RequestUtil.getCurrentUserId();
        String refreshToken = redisUtil.getCacheObject(CacheConst.LOGIN_TOKEN_REFRESH_KEY + userId);
        if (refreshToken == null || !RequestUtil.getToken().equals(refreshToken)) {
            throw new ServiceException(HttpStatus.BAD_REQUEST, "请求token过期，请重新登录");
        }
        User loginUser = redisUtil.getCacheObject(CacheConst.LOGIN_USER_KEY + userId);
        if(loginUser == null){
            throw new ServiceException("请求错误");
        }
        return createToken(loginUser);
    }
    @Resource
    UserMapper userMapper;

    @Resource
    TeamMapper teamMapper;

    @Resource
    TeamRoleMapper teamRoleMapper;

    @Override
    public List<TeamUserVO> getAllByTeamId(Integer id) {
        List<TeamUserVO> teamUserVOS = new ArrayList<>();
        List<User> users = userMapper.getAllByTeamId(id);
        for(User record : users){
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(record,teamUserVO);

            int role = teamMapper.getRoleByTeamId(record.getId(),id);
            String roleName = teamRoleMapper.getNameById(role);
            teamUserVO.setRole(roleName);
            teamUserVOS.add(teamUserVO);
        }


        return teamUserVOS;
    }

    @Override
    public Object getByTeamId(Integer id) {
        User root = userMapper.getByTeamId(id);
        if(root == null)
        {
            throw new ServiceException("您查找的团队不存在，请重新选择团队进入");
        }
        return root;
    }

}
